// copyright 1993 Michael B. Johnson; some portions copyright 1994, MIT
// see COPYRIGHT for reuse legalities
//

#import "RIBTorus.h"
#import <stdio.h>


@implementation RIBTorus

+ initialize { return [RIBTorus setVersion:1], self; }

- (BOOL)hasBoundingBox { return YES; }

- init
{
  [super init];

  majorRadius = 1.0;
  minorRadius = 0.5;
  phiMin = 0.0;
  phiMax =  360.0;
  
  return self;
}

- setMajorRadius:(RtFloat)newMajorRadius minorRadius:(RtFloat)newMinorRadius
     phiMin:(RtFloat)newPhiMin phiMax:(RtFloat)newPhiMax
     thetaMax:(RtFloat)newThetaMax 
     n:(int)newN tokens:(RtToken *)newTokens parms:(RtPointer *)newParms archiveVector:(char **)newArchiveVector
     printfTypeVector:(int *)newPrintfTypeVector printfNVector:(int *)newPrintfNVector
{  
   majorRadius = newMajorRadius;
   minorRadius = newMinorRadius;
   phiMin = newPhiMin;
   phiMax = newPhiMax;
   thetaMax = newThetaMax;
   [self setN:newN tokens:newTokens parms:newParms archiveVector:newArchiveVector printfTypeVector:newPrintfTypeVector printfNVector:newPrintfNVector];

   dirtyBoundingBox = TRUE;
  
   return self;
}

- setMajorRadius:(RtFloat)newMajorRadius { majorRadius = newMajorRadius; dirtyBoundingBox = TRUE; return self; }
- setMinorRadius:(RtFloat)newMinorRadius { minorRadius = newMinorRadius; dirtyBoundingBox = TRUE; return self; }
- setPhiMin:(RtFloat)newPhiMin { phiMin = newPhiMin; dirtyBoundingBox = TRUE; return self; }
- setPhiMax:(RtFloat)newPhiMax { phiMax = newPhiMax; dirtyBoundingBox = TRUE; return self; }

- (RtFloat)majorRadius { return majorRadius; } 
- (RtFloat)minorRadius { return minorRadius; } 
- (RtFloat)phiMin { return phiMin; }
- (RtFloat)phiMax { return phiMax; }

- (BOOL)hasboundingBox { return YES; }

- calculateBoundingBoxStartingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
{
  // WAVE: FILL ME IN CORRECTLY!!!
  boundingBox[0] =  -1.0 *  (majorRadius + minorRadius);
  boundingBox[1] = majorRadius + minorRadius;

  if (thetaMax >= 270.0)
  {  boundingBox[2] =  -1.0 *  (majorRadius + minorRadius);
     boundingBox[3] = majorRadius + minorRadius;
  }
  else
  {  if (thetaMax >= 180.0)
     {  boundingBox[2] = (majorRadius + minorRadius) * sin((double)(toRadians((thetaMax))));
        boundingBox[3] = (majorRadius + minorRadius);
     }
     else
     {  if (thetaMax >= 90.0)
        {  boundingBox[2] = 0.0;
           boundingBox[3] = (majorRadius + minorRadius);
        }
        else
        {  boundingBox[2] = 0.0;
           boundingBox[3] = (majorRadius + minorRadius) * sin((double)(toRadians((thetaMax))));
        }
     }
  }

  boundingBox[4] = -1.0 * minorRadius;
  boundingBox[5] = minorRadius;
  dirtyBoundingBox = FALSE; 

  return self;
}

- renderSelf:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
{
  RiTorusV(majorRadius, minorRadius, phiMin, phiMax, thetaMax, n, tokens, parms);

  return self;
}

- (BOOL)theSameAs:otherRIBCommand
{
  if (minorRadius != [otherRIBCommand minorRadius])
  {  return NO;
  }
  if (majorRadius != [otherRIBCommand majorRadius])
  {  return NO;
  }
  if (phiMin != [otherRIBCommand phiMin])
  {  return NO;
  }
  if (phiMax != [otherRIBCommand phiMax])
  {  return NO;
  }

  return [super theSameAs:otherRIBCommand];
}

- (BOOL)isLerpable { return YES; }

// note: because we've made the WWSampleList "safe" for having
// multiple samples with the same data, it's perfectly valid to return
// yourself or b
- lerpWith:b by:(float)uValue
{
   id      newMe = nil;
   RtFloat minorRadiusA, majorRadiusA, phiMinA, phiMaxA, thetaMaxA;
   RtFloat minorRadiusB, majorRadiusB, phiMinB, phiMaxB, thetaMaxB;


   if (([self class] != [b class]) || (uValue <= 0.0))
   {  return self;
   }

   if (uValue >= 1.0)
   {  return b;
   }

   newMe = [super lerpWith:b by:uValue]; // this makes a copy for us

   // okay, now do the specific stuff for this class
   minorRadiusA = [self minorRadius];
   minorRadiusB = [b minorRadius];
   [newMe setMinorRadius:(minorRadiusA + ((minorRadiusB - minorRadiusA) * uValue))]; 
   majorRadiusA = [self majorRadius];
   majorRadiusB = [b majorRadius];
   [newMe setMajorRadius:(majorRadiusA + ((majorRadiusB - majorRadiusA) * uValue))]; 
   phiMinA = [self phiMin];
   phiMinB = [b phiMin];
   [newMe setPhiMin:(phiMinA + ((phiMinB - phiMinA) * uValue))];
   phiMaxA = [self phiMax];
   phiMaxB = [b phiMax];
   [newMe setPhiMax:(phiMaxA + ((phiMaxB - phiMaxA) * uValue))];
   thetaMaxA = [self thetaMax];
   thetaMaxB = [b thetaMax];
   [newMe setThetaMax:(thetaMaxA + ((thetaMaxB - thetaMaxA) * uValue))];
 
   return newMe;
}


- lerpSelfWith:b by:(float)uValue
{
   RtFloat minorRadiusA, majorRadiusA, phiMinA, phiMaxA, thetaMaxA;
   RtFloat minorRadiusB, majorRadiusB, phiMinB, phiMaxB, thetaMaxB;


   if (([self class] != [b class]) || (uValue <= 0.0))
   {  return self;
   }

   if (uValue >= 1.0)
   {  return b;
   }

   [super lerpSelfWith:b by:uValue]; // this makes a copy for us

   // okay, now do the specific stuff for this class
   minorRadiusA = [self minorRadius];
   minorRadiusB = [b minorRadius];
   [self setMinorRadius:(minorRadiusA + ((minorRadiusB - minorRadiusA) * uValue))]; 
   majorRadiusA = [self majorRadius];
   majorRadiusB = [b majorRadius];
   [self setMajorRadius:(majorRadiusA + ((majorRadiusB - majorRadiusA) * uValue))]; 
   phiMinA = [self phiMin];
   phiMinB = [b phiMin];
   [self setPhiMin:(phiMinA + ((phiMinB - phiMinA) * uValue))];
   phiMaxA = [self phiMax];
   phiMaxB = [b phiMax];
   [self setPhiMax:(phiMaxA + ((phiMaxB - phiMaxA) * uValue))];
   thetaMaxA = [self thetaMax];
   thetaMaxB = [b thetaMax];
   [self setThetaMax:(thetaMaxA + ((thetaMaxB - thetaMaxA) * uValue))];

   return self;
}

- writeEve:(NXStream *)stream atTabLevel:(int)tab
{
   int  i;


   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "Torus %f %f %f %f %f", majorRadius, minorRadius, phiMin, phiMax, thetaMax);
   [super writeParameterList:stream];
   return self;
}

- writeInventorAtTime:(float)currentTime to:(NXStream *)stream atTabLevel:(int)tab
{
   RtFloat umax;
   RtFloat uknots[12];
   RtFloat vmin, vmax;
   RtFloat vknots[12];
   RtFloat knots[12] = { 0.0, 0.0, 0.0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1.0, 1.0, 1.0};
   RtFloat uspline[9][4] = { {1.0, 0.0, 1.0, 1.0},
			     {M_SQRT1_2, M_SQRT1_2, M_SQRT1_2, M_SQRT1_2},
			     {0.0, 1.0, 1.0, 1.0},
			     {-M_SQRT1_2, M_SQRT1_2, M_SQRT1_2, M_SQRT1_2},
			     {-1.0, 0.0, 1.0, 1.0},
			     {-M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2, M_SQRT1_2},
			     {0.0, -1.0, 1.0, 1.0},
			     {M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2, M_SQRT1_2},
			     {1.0, 0.0, 1.0, 1.0}};
   RtFloat vspline[9][4];
   int i, j, k;


   umax = [self thetaMax] / 360.0;
   vmin = [self phiMin] / 360.0;
   vmax = [self phiMax] / 360.0;
   for (i=0; i<12; i++)
   {  uknots[i] = knots[i] / umax;
      vknots[i] = (knots[i] - vmin) / (vmax - vmin);
   }

   vspline[0][0] = [self majorRadius] - [self minorRadius];
   vspline[0][1] = [self majorRadius] - [self minorRadius];
   vspline[0][2] = 0.0;
   vspline[0][3] = 1.0;
   vspline[1][0] = ([self majorRadius] - [self minorRadius]) * M_SQRT1_2;
   vspline[1][1] = ([self majorRadius] - [self minorRadius]) * M_SQRT1_2;
   vspline[1][2] = -[self minorRadius] * M_SQRT1_2;
   vspline[1][3] = M_SQRT1_2;
   vspline[2][0] = [self majorRadius];
   vspline[2][1] = [self majorRadius];
   vspline[2][2] = -[self minorRadius];
   vspline[2][3] = 1.0;
   vspline[3][0] = ([self majorRadius] + [self minorRadius]) * M_SQRT1_2;
   vspline[3][1] = ([self majorRadius] + [self minorRadius]) * M_SQRT1_2;
   vspline[3][2] = -[self minorRadius] * M_SQRT1_2;
   vspline[3][3] = M_SQRT1_2;
   vspline[4][0] = [self majorRadius] + [self minorRadius];
   vspline[4][1] = [self majorRadius] + [self minorRadius];
   vspline[4][2] = 0.0;
   vspline[4][3] = 1.0;
   vspline[5][0] = ([self majorRadius] + [self minorRadius]) * M_SQRT1_2;
   vspline[5][1] = ([self majorRadius] + [self minorRadius]) * M_SQRT1_2;
   vspline[5][2] = [self minorRadius] * M_SQRT1_2;
   vspline[5][3] = M_SQRT1_2;
   vspline[6][0] = [self majorRadius];
   vspline[6][1] = [self majorRadius];
   vspline[6][2] = [self minorRadius];
   vspline[6][3] = 1.0;
   vspline[7][0] = ([self majorRadius] - [self minorRadius]) * M_SQRT1_2;
   vspline[7][1] = ([self majorRadius] - [self minorRadius]) * M_SQRT1_2;
   vspline[7][2] = [self minorRadius] * M_SQRT1_2;
   vspline[7][3] = M_SQRT1_2;
   vspline[8][0] = [self majorRadius] - [self minorRadius];
   vspline[8][1] = [self majorRadius] - [self minorRadius];
   vspline[8][2] = 0.0;
   vspline[8][3] = 1.0;

   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "# ");
   [self writeEve:stream atTabLevel:0];
   NXPrintf(stream, "\n");

   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "# since Inventor doesn't directly support quadrics in any terribly useful way (sigh), we turn it into a NURBS surface:\n");

   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "Separator {\n");

   for (i = 0; i < (tab+1); i++)  {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "Complexity {\n");
   for (i = 0; i < (tab+2); i++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "value 1.0\n");
   for (i = 0; i < (tab+1); i++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "}\n");

   for (i = 0; i < (tab+1); i++)  {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "Coordinate4 {\n");
   for (i = 0; i < (tab+2); i++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "point [");

   for (i=0; i<8; i++)
   {  for (j=0; j<9; j++)
      {  if (!i && !j)
         {  NXPrintf(stream, 
		     "%f %f %f %f,\n", 
		     (uspline[j][0] * vspline[i][0]),
		     (uspline[j][1] * vspline[i][1]),
		     (uspline[j][2] * vspline[i][2]),
		     (uspline[j][3] * vspline[i][3]));
 	 }
         else
         {  NXPrintf(stream, 
		     "       %f %f %f %f,\n", 
		     (uspline[j][0] * vspline[i][0]),
		     (uspline[j][1] * vspline[i][1]),
		     (uspline[j][2] * vspline[i][2]),
		     (uspline[j][3] * vspline[i][3]));
 	 }
         for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
      }
   }
   for (j=0; j<8; j++)
   {  NXPrintf(stream, 
               "       %f %f %f %f,\n", 
               (uspline[j][0] * vspline[i][0]),
               (uspline[j][1] * vspline[i][1]),
               (uspline[j][2] * vspline[i][2]),
               (uspline[j][3] * vspline[i][3]));
     for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   }
   NXPrintf(stream, 
	    "       %f %f %f %f]\n", 
	    (uspline[j][0] * vspline[i][0]),
	    (uspline[j][1] * vspline[i][1]),
	    (uspline[j][2] * vspline[i][2]),
	    (uspline[j][3] * vspline[i][3]));
   for (k = 0; k < (tab+1); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "}\n");

   for (k = 0; k < (tab+1); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "NurbsSurface {\n");
   for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "numUControlPoints 9\n");
   for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "numVControlPoints 2\n");

   for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "uKnotVector [");
   for (i=0; i<11; i++)
   {	NXPrintf(stream, "%f, ", uknots[i]);
   }
   NXPrintf(stream, "%f]\n", uknots[i]);

   for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "vKnotVector [");
   for (i=0; i<3; i++)
   {	NXPrintf(stream, "%f, ", vknots[i]);
   }
   NXPrintf(stream, "%f]\n", vknots[i]);
   for (k = 0; k < (tab+1); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "}\n");
   for (k = 0; k < tab; k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "}\n");

   return self;
}


#define typeVector "ffff"
#define typeValues &majorRadius, &minorRadius, &phiMin, &phiMax

- read:(NXTypedStream *)stream 
{
    int version;
    [super read:stream];

    version = NXTypedStreamClassVersion(stream,"RIBTorus");
    if (version == 0) NXReadTypes(stream,"i",&version), version=1;
    if (version == 1)
    {  NXReadTypes(stream,typeVector,typeValues);
    } 
    else 
    {
    }
    return self;
}

- write:(NXTypedStream *)stream 
{
    [super write:stream];

    NXWriteTypes(stream,typeVector, typeValues);

    return self;
}



@end
